#include "base/mutex.h"
#include "base/logging.h"
#include "gtest/gtest.h"
#include "thread/thread.h"

int global_val = 0;

class Thread1 : public Thread {
 public:
  Thread1(Mutex* mutex, int val) {
    mutex_ = mutex;
    val_ = val;
  }
  void Run() {
    mutex_->WriteLock();
    global_val = val_;
    mutex_->WriteUnlock();
  }
 private:
  Mutex* mutex_;
  int val_;
};

TEST(MutexTest, TestMutex) {
  global_val = 0;
  Mutex mutex;
  Thread1 t1(&mutex, 1000);
  t1.Start();
  t1.Join();
  EXPECT_EQ(1000, global_val);
}

TEST(MutexTest, TestMutexLock) {
  global_val = 0;
  Mutex mutex;
  mutex.Lock();
  Thread1 t1(&mutex, 1000);
  t1.Start();
  EXPECT_EQ(0, global_val);
  sleep(1);
  EXPECT_EQ(0, global_val);
  mutex.Unlock();
  sleep(1);
  EXPECT_EQ(1000, global_val);
  t1.Join();
}

class ThreadPrint : public Thread {
 public:
  ThreadPrint(Mutex* mutex, int val, bool read_or_write) {
    mutex_ = mutex;
    val_ = val;
    read_or_write_ = read_or_write;
  }
  void Run() {
    if (read_or_write_) {
      mutex_->ReadLock();
    } else {
      mutex_->WriteLock();
    }
    for (int i = 0; i < 5; ++i) {
      LOG(INFO) << val_;
      sleep(1);
    }
    if (read_or_write_) {
      mutex_->ReadUnlock();
    } else {
      mutex_->WriteUnlock();
    }
  }
 private:
  Mutex* mutex_;
  int val_;
  bool read_or_write_;
};
TEST(MutexTest, TestReadCouldRunConcurrently) {
  global_val = 0;
  Mutex mutex;
  ThreadPrint t1(&mutex, 1, true);
  ThreadPrint t2(&mutex, 2, true);
  ThreadPrint t3(&mutex, 3, false);
  ThreadPrint t4(&mutex, 4, true);
  t1.Start();
  t2.Start();
  sleep(1);
  t3.Start();
  sleep(1);
  t4.Start();
  t1.Join();
  t2.Join();
  t3.Join();
  t4.Join();
}
